using System;
using System.Data;
using System.Data.Common;
using System.Configuration;
using System.Configuration.Provider;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Collections.Generic;

namespace XZ.Logistics.WebShop
{
    public abstract class WebShopProvider : ProviderBase
    {
        // Properties
        public abstract string ApplicationName { get; set; }

        // methods that work with departments
        public abstract List<Category> GetCategories();
        public abstract Category GetCategoryById(Guid categoryId);
        public abstract bool DeleteCategory(Guid categoryId);
        public abstract bool UpdateCategory(Category category);
        public abstract Guid InsertCategory(Category category);

        // methods that work with order statuses
        public abstract List<OrderStatus> GetOrderStatuses();
        public abstract OrderStatus GetOrderStatusById(int orderStatusId);
        public abstract bool DeleteOrderStatus(int orderStatusId);
        public abstract bool UpdateOrderStatus(OrderStatus orderStatus);
        public abstract int InsertOrderStatus(OrderStatus orderStatus);

        // methods that work with shipping methods
        public abstract List<ShippingMethod> GetShippingMethods();
        public abstract ShippingMethod GetShippingMethodById(Guid shippingMethodId);
        public abstract bool DeleteShippingMethod(Guid shippingMethodId);
        public abstract bool UpdateShippingMethod(ShippingMethod shippingMethod);
        public abstract Guid InsertShippingMethod(ShippingMethod shippingMethod);

        // methods that work with products
        public abstract List<Product> GetProducts(string sortExpression, int pageIndex, int pageSize);
        public abstract List<Product> GetProducts(Guid categoryId, string sortExpression, int pageIndex, int pageSize);
        public abstract int GetProductCount();
        public abstract int GetProductCount(Guid categoryId);
        public abstract Product GetProductById(Guid productId);
        public abstract bool DeleteProduct(Guid productId);
        public abstract bool UpdateProduct(Product product);
        public abstract Guid InsertProduct(Product product);
        public abstract bool RateProduct(Guid productId, int rating);
        public abstract bool DecrementProductUnitsInStock(Guid productId, int quantity);
        public abstract string GetProductDescription(Guid productId);

        // methods that work with orders
        public abstract List<Order> GetOrders(int statusId, DateTime fromDate, DateTime toDate);
        public abstract List<Order> GetOrders(string addedBy);
        public abstract Order GetOrderById(Guid orderId);
        public abstract bool DeleteOrder(Guid orderId);
        public abstract Guid InsertOrder(Order order);
        public abstract bool UpdateOrder(Order order);

        // methods that work with order items
        public abstract List<OrderItem> GetOrderItems(Guid orderId);
        public abstract Guid InsertOrderItem(OrderItem orderItem);

        /// <summary>
        /// Returns a valid sort expression for the products
        /// </summary>
        protected virtual string EnsureValidProductsSortExpression(string sortExpression)
        {
            if (string.IsNullOrEmpty(sortExpression))
                return "xz_Products.Title ASC";

            string sortExpr = sortExpression.ToLower();
            if (!sortExpr.Equals("unitprice") && !sortExpr.Equals("unitprice asc") && !sortExpr.Equals("unitprice desc") &&
               !sortExpr.Equals("discountpercentage") && !sortExpr.Equals("discountpercentage asc") && !sortExpr.Equals("discountpercentage desc") &&
               !sortExpr.Equals("addeddate") && !sortExpr.Equals("addeddate asc") && !sortExpr.Equals("addeddate desc") &&
               !sortExpr.Equals("addedby") && !sortExpr.Equals("addedby asc") && !sortExpr.Equals("addedby desc") &&
               !sortExpr.Equals("unitsinstock") && !sortExpr.Equals("unitsinstock asc") && !sortExpr.Equals("unitsinstock desc") &&
               !sortExpr.Equals("title") && !sortExpr.Equals("title asc") && !sortExpr.Equals("title desc"))
            {
                sortExpr = "title asc";
            }
            if (!sortExpr.StartsWith("tbh_products"))
                sortExpr = "xz_products." + sortExpr;
            if (!sortExpr.StartsWith("tbh_products.title"))
                sortExpr += ", xz_products.title asc";
            return sortExpr;
        }

        /// <summary>
        /// Returns a new Category instance filled with the DataReader's current record data
        /// </summary>
        protected virtual Category GetCategoryFromReader(IDataReader reader)
        {
            return new Category(
               (Guid)reader["CategoryId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               reader["Title"].ToString(),
               (int)reader["Importance"],
               reader["Description"].ToString(),
               reader["ImageUrl"].ToString());
        }

        /// <summary>
        /// Returns a collection of DepartmentDetails objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<Category> GetCategoryCollectionFromReader(IDataReader reader)
        {
            List<Category> departments = new List<Category>();
            while (reader.Read())
                departments.Add(GetCategoryFromReader(reader));
            return departments;
        }

        /// <summary>
        /// Returns a new OrderStatus instance filled with the DataReader's current record data
        /// </summary>
        protected virtual OrderStatus GetOrderStatusFromReader(IDataReader reader)
        {
            return new OrderStatus(
               (int)reader["OrderStatusId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               reader["Title"].ToString());
        }

        /// <summary>
        /// Returns a collection of OrderStatus objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<OrderStatus> GetOrderStatusCollectionFromReader(IDataReader reader)
        {
            List<OrderStatus> orderStatuses = new List<OrderStatus>();
            while (reader.Read())
                orderStatuses.Add(GetOrderStatusFromReader(reader));
            return orderStatuses;
        }

        /// <summary>
        /// Returns a new ShippingMethod instance filled with the DataReader's current record data
        /// </summary>
        protected virtual ShippingMethod GetShippingMethodFromReader(IDataReader reader)
        {
            return new ShippingMethod(
               (Guid)reader["ShippingMethodId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               reader["Title"].ToString(),
               (decimal)reader["Price"]);
        }

        /// <summary>
        /// Returns a collection of ShippingMethod objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<ShippingMethod> GetShippingMethodCollectionFromReader(IDataReader reader)
        {
            List<ShippingMethod> shippingMethods = new List<ShippingMethod>();
            while (reader.Read())
                shippingMethods.Add(GetShippingMethodFromReader(reader));
            return shippingMethods;
        }

        /// <summary>
        /// Returns a new Product instance filled with the DataReader's current record data
        /// </summary>
        protected virtual Product GetProductFromReader(IDataReader reader)
        {
            return GetProductFromReader(reader, true);
        }
        protected virtual Product GetProductFromReader(IDataReader reader, bool readDescription)
        {
            Product product = new Product(
               (Guid)reader["ProductId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               (Guid)reader["CategoryId"],
               reader["CategoryTitle"].ToString(),
               reader["Title"].ToString(),
               null,
               reader["SKU"].ToString(),
               (decimal)reader["UnitPrice"],
               (int)reader["DiscountPercentage"],
               (int)reader["UnitsInStock"],
               reader["SmallImageUrl"].ToString(),
               reader["FullImageUrl"].ToString(),
               (int)reader["Votes"],
               (int)reader["TotalRating"]);

            if (readDescription)
                product.Description = reader["Description"].ToString();

            return product;
        }

        /// <summary>
        /// Returns a collection of ProductDetails objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<Product> GetProductCollectionFromReader(IDataReader reader)
        {
            return GetProductCollectionFromReader(reader, true);
        }
        protected virtual List<Product> GetProductCollectionFromReader(IDataReader reader, bool readDescription)
        {
            List<Product> products = new List<Product>();
            while (reader.Read())
                products.Add(GetProductFromReader(reader, readDescription));
            return products;
        }

        /// <summary>
        /// Returns a new OrderItem instance filled with the DataReader's current record data
        /// </summary>
        protected virtual OrderItem GetOrderItemFromReader(IDataReader reader)
        {
            return new OrderItem(
               (Guid)reader["OrderItemId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               (Guid)reader["OrderID"],
               (Guid)reader["ProductId"],
               reader["Title"].ToString(),
               reader["SKU"].ToString(),
               (decimal)reader["UnitPrice"],
               (int)reader["Quantity"]);
        }

        /// <summary>
        /// Returns a collection of OrderItem objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<OrderItem> GetOrderItemCollectionFromReader(IDataReader reader)
        {
            List<OrderItem> orderItems = new List<OrderItem>();
            while (reader.Read())
                orderItems.Add(GetOrderItemFromReader(reader));
            return orderItems;
        }

        /// <summary>
        /// Returns a new OrderDetails instance filled with the DataReader's current record data
        /// </summary>
        protected virtual Order GetOrderFromReader(IDataReader reader)
        {
            return new Order(
               (Guid)reader["OrderId"],
               (DateTime)reader["AddedDate"],
               reader["AddedBy"].ToString(),
               (int)reader["StatusId"],
               reader["StatusTitle"].ToString(),
               reader["ShippingMethod"].ToString(),
               (decimal)reader["SubTotal"],
               (decimal)reader["Shipping"],
               reader["ShippingFirstName"].ToString(),
               reader["ShippingLastName"].ToString(),
               reader["ShippingStreet"].ToString(),
               reader["ShippingPostalCode"].ToString(),
               reader["ShippingCity"].ToString(),
               reader["ShippingState"].ToString(),
               reader["ShippingCountry"].ToString(),
               reader["CustomerEmail"].ToString(),
               reader["CustomerPhone"].ToString(),
               reader["CustomerFax"].ToString(),
               (reader["ShippedDate"] == DBNull.Value ? DateTime.MinValue : (DateTime)reader["ShippedDate"]),
               reader["TransactionId"].ToString(),
               reader["TrackingId"].ToString(),
               null);
        }

        /// <summary>
        /// Returns a collection of OrderDetails objects with the data read from the input DataReader
        /// </summary>
        protected virtual List<Order> GetOrderCollectionFromReader(IDataReader reader)
        {
            List<Order> orders = new List<Order>();
            while (reader.Read())
                orders.Add(GetOrderFromReader(reader));
            return orders;
        }

        protected int ExecuteNonQuery(DbCommand cmd)
        {
            if (HttpContext.Current.User.Identity.Name.ToLower() == "sampleeditor")
            {
                foreach (DbParameter param in cmd.Parameters)
                {
                    if (param.Direction == ParameterDirection.Output ||
                       param.Direction == ParameterDirection.ReturnValue)
                    {
                        switch (param.DbType)
                        {
                            case DbType.AnsiString:
                            case DbType.AnsiStringFixedLength:
                            case DbType.String:
                            case DbType.StringFixedLength:
                            case DbType.Xml:
                                param.Value = "";
                                break;
                            case DbType.Boolean:
                                param.Value = false;
                                break;
                            case DbType.Byte:
                                param.Value = byte.MinValue;
                                break;
                            case DbType.Date:
                            case DbType.DateTime:
                                param.Value = DateTime.MinValue;
                                break;
                            case DbType.Currency:
                            case DbType.Decimal:
                                param.Value = decimal.MinValue;
                                break;
                            case DbType.Guid:
                                param.Value = Guid.Empty;
                                break;
                            case DbType.Double:
                            case DbType.Int16:
                            case DbType.Int32:
                            case DbType.Int64:
                                param.Value = 0;
                                break;
                            default:
                                param.Value = null;
                                break;
                        }
                    }
                }
                return 1;
            }
            else
                return cmd.ExecuteNonQuery();
        }

        protected IDataReader ExecuteReader(DbCommand cmd)
        {
            return ExecuteReader(cmd, CommandBehavior.Default);
        }

        protected IDataReader ExecuteReader(DbCommand cmd, CommandBehavior behavior)
        {
            return cmd.ExecuteReader(behavior);
        }

        protected object ExecuteScalar(DbCommand cmd)
        {
            return cmd.ExecuteScalar();
        }
    }
}